# -*- coding:utf-8 -*-

# @Time    : 2023/8/31 13:54
# @Author  : zengwenjia
# @Email   : zengwenjia@lingxi.ai
# @File    : sales.py
# @Software: LLM_internal

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
import json
import random

from bot.bot import Bot
from bot.insurance_sales.agent.conversation_agent import Conversation
from bot.insurance_sales.agent.sales_skill_agent import SalesSkill, get_sales_skill_utter
from bot.insurance_sales.province_city_dict import ProvinceCityDict
from common.log import logger
import asyncio
import nest_asyncio
nest_asyncio.apply()
from common import constants
from bot.insurance_sales.agent.user_questioning import QuestioningDisputeResolution
from bot.insurance_sales.agent.user_info_extract import UserInfoExtract
# from bot.insurance_sales.agent.determine_solution import DetermineSolution
# from bot.insurance_sales.agent.distance_problem_agent import DistanceProblemAgent
# from bot.insurance_sales.agent.generate_test_set import GenerateTestSet
# from bot.insurance_sales.agent.transfer_hangup import TransferHangup
from bot.insurance_sales.knowledge_base.knowledge_embedding import AZUREQuery, BGEQuery
from bot.insurance_sales.user_info import UserInfo
from bot.insurance_sales_gpt.demo.cache_dict import cache_dict


class Sales(Bot):
    """销售机器人"""
    knowledge_base_query = BGEQuery()
    knowledge_base_query.load_knowledge_base()

    def __init__(self):
        cities = ['成都', '重庆', '北京', '广州', '安庆', '深圳', '沈阳', '天津', '杭州',
                  '宁波', '大连', '青岛', '苏州', '南京', '长沙', '长春', '济南', '郑州',
                  '武汉', '合肥', '无锡', '佛山', '石家庄', '南宁', '东莞', '南昌', '西安',
                  '眉山', '太原', '烟台', '潍坊', '唐山', '福州', '南通', '淄博', '扬州',
                  '常州', '厦门', '济宁', '中山', '绍兴', '芜湖', '洛阳', '惠州', '资阳',
                  '泉州', '镇江', '海口', '泰州', '六安', '徐州', '珠海', '盐城', '九江',
                  '吉林', '贵阳', '东营', '台州', '湘潭', '宜春', '保定', '上饶', '湖州',
                  '四平', '嘉兴', '临沂', '株洲', '德州', '绵阳', '廊坊', '泰安', '菏泽',
                  '宿迁', '威海', '广安']
        self.max_token_length = 1024
        self.role_name = "泰康的客户经理"
        self.model_name = "gpt"
        self.city = random.choice(cities)
        self.skill = ""
        self.user_info = ""
        self.steam = False

    def format_conversation_history(self, conversation_context, max_token_length=None):
        dialogue_history = ""
        if (not max_token_length) or (max_token_length > self.max_token_length):
            max_token_length = self.max_token_length
        # 倒序遍历record_list
        for record in conversation_context[::-1]:
            next_dialogue_history = dialogue_history
            if record['role'] == constants.ROLE_USER:
                next_dialogue_history = "用户:" + "".join([str(i) for i in record["content"]]) + "\n" + dialogue_history
            elif record['role'] == constants.ROLE_ASSISTANT:
                next_dialogue_history = self.role_name + ":" + "".join(
                    [str(i) for i in record["content"]]) + "\n" + dialogue_history
            if len(next_dialogue_history) > max_token_length:
                break
            else:
                dialogue_history = next_dialogue_history
        return dialogue_history

    async def get_user_question(self):
        try:
            questioning_dispute_resolution = QuestioningDisputeResolution(self.role_name, self.conversation_history_str,
                                                                          self.city)
            self.user_question = await questioning_dispute_resolution.achat_auto_llm(type=self.model_name)
            logger.info(f"session_id:{self.session_id},整理后的用户问题是:{self.user_question}")
        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            self.user_question = ""

    # async def get_sales_skill(self):
    #     try:
    #         sales_skill_resolution = SalesSkill(self.role_name,
    #                                                                       self.conversation_history_str)
    #         self.sales_skill = await sales_skill_resolution.achat_auto_llm(type=self.model_name)
    #         logger.info(f"session_id:{self.session_id},整理后的下一步流程是:{self.sales_skill}")
    #         process_knowledge = self.knowledge_base_query.search_with_score("流程:" + self.sales_skill, top_k=4,
    #                                                                            limit_score=0.8)
    #         background_knowledge = []
    #         if process_knowledge:
    #             # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
    #             for item in process_knowledge:
    #                 background_knowledge.append(item.page_content)
    #                 if len("".join(background_knowledge)) > 1024:
    #                     break
    #         self.background_knowledge = background_knowledge
    #     except Exception as e:
    #         # 打印错误的堆栈信息
    #         logger.exception(e)
    #         self.sales_skill = ""

    async def extract_user_info(self, session_id):
        user_info_rule = UserInfo.user_info_rule
        try:
            user_info_extract = UserInfoExtract(self.role_name, self.conversation_history_str, user_info_rule,
                                                self.city, self.user_info)
            self.user_info = await user_info_extract.achat_auto_llm(type=self.model_name)
            logger.info(f"session_id:{session_id},整理后的用户信息是:{self.user_info}")
            cache_dict['user_info-' + session_id] = self.user_info
        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            self.user_info = ""

    # async def determine_solution(self, conversation, session_id):
    #     try:
    #         determine_solution = DetermineSolution(self.role_name, self.user_question, self.background_knowledge,
    #                                                conversation)
    #         self.solution = await determine_solution.achat_auto_llm(type=self.model_name)
    #         logger.info(f"session_id:{session_id},整理后的解决方案是:{self.solution}")
    #         return self.solution
    #     except Exception as e:
    #         # 打印错误的堆栈信息
    #         logger.exception(e)
    #         self.solution = ""

    # async def distance_problem_agent(self, conversation, session_id):
    #     try:
    #         distance_problem = DistanceProblemAgent(self.role_name, self.user_info, conversation)
    #         self.distance_problem = await distance_problem.achat_auto_llm(type=self.model_name)
    #         logger.info(f"session_id:{session_id},整理后的距离太远的沟通策略是:{self.distance_problem}")
    #         similarity_knowledge = self.knowledge_base_query.search_with_score("用户问题:" + self.distance_problem,
    #                                                                            top_k=5, limit_score=1.2)
    #         # process_knowledge = self.knowledge_base_query.search_with_score("主流程:", top_k=10, limit_score=0.9)
    #         background_knowledge = []
    #         if similarity_knowledge:
    #             # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
    #             for item in similarity_knowledge:
    #                 background_knowledge.append(item.page_content)
    #                 if len("".join(background_knowledge)) > 1024:
    #                     break
    #         self.background_knowledge = background_knowledge
    #         return self.distance_problem
    #     except Exception as e:
    #         # 打印错误的堆栈信息
    #         logger.exception(e)
    #         self.distance_problem = ""

    # async def transfer_hangup(self, conversation, session_id, result):
    #     try:
    #         transfer_hangup = TransferHangup(self.role_name, conversation, self.skill, result)
    #         self.transfer_hangup = await transfer_hangup.achat_auto_llm(type=self.model_name)
    #         logger.info(f"session_id:{session_id},判断转人或挂机的结果是:{self.transfer_hangup}")
    #         return self.transfer_hangup
    #     except Exception as e:
    #         # 打印错误的堆栈信息
    #         logger.exception(e)
    #         self.transfer_hangup = ""

    async def sales_skill(self, conversation, session_id):
        try:
            sales_skill = SalesSkill(self.role_name, conversation, self.user_info)
            self.skill = await sales_skill.achat_auto_llm(type=self.model_name)
            if self.skill:
                # 删除返回的前后空挂
                self.skill = self.skill.strip()
            logger.info(f"session_id:{session_id},整理后的下一步流程是:{self.skill}")
            # process_knowledge = self.knowledge_base_query.search_with_score("主流程:" + self.skill, top_k=6,
            #                                                                    limit_score=0.8)
            self.process_knowledge = get_sales_skill_utter(self.skill)
            # background_knowledge = []
            # if process_knowledge:
            #     # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
            #     for item in process_knowledge:
            #         background_knowledge.append(item.page_content)
            #         if len("".join(background_knowledge)) > 1024:
            #             break
            # self.process_knowledge = background_knowledge
        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            self.skill = ""

    def generate_reply(self):
        try:
            conversation = Conversation(self.role_name,
                                        self.conversation_history_str, self.background_knowledge,
                                        self.process_knowledge, self.user_info, self.user_question)
            result = conversation.achat_auto_llm(type=self.model_name, is_stream=self.steam)
            return result
        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            return "出错了，我不知道怎么回答你的问题，你可以换个问法试试"

    async def async_reply(self, context, user_info, session_id):
        self.user_info = user_info
        conversation = self.format_conversation_history(context)
        logger.info("conversation is " + str(conversation) + " session_id is " + str(session_id))
        self.session_id = session_id
        self.context = context
        self.conversation_history_str = conversation

        if len(self.context) > 1:
            extract_user_info = self.extract_user_info(session_id)
            get_user_question = self.get_user_question()
            # if ("距离太远" in self.user_question) or ("距离问题" in self.user_question) or (
            #         "太远" in self.user_question):
            #     await self.distance_problem_agent(conversation, session_id)
            # await self.determine_solution(conversation, session_id)

            sales_skill = self.sales_skill(conversation, session_id)
            # 并行执行 用户情景、用户问题、销售技巧
            await asyncio.gather(extract_user_info, get_user_question, sales_skill)
            # 查询知识库
            self.query_background_knowledge()
        else:
            return "喂，您好，您好，我这边是泰康高品质养老社区体验官，给您来电是想邀请您和家人来参观咱们泰康的高品质养老社区，提前为养老做规划！耽误您2分钟简单给您做个介绍好吧？"
            # self.background_knowledge = ""
            # self.process_knowledge = "喂，您好，您好，我这边是泰康高品质养老社区体验官，给您来电是想邀请您和家人来参观咱们泰康的高品质养老社区，提前为养老做规划！耽误您2分钟简单给您做个介绍好吧？"
            # self.solution = ""
        logger.info(
            f"session_id:{self.session_id},Sales Skill: {self.process_knowledge},Determine Solution: {self.background_knowledge}")

        result = await self.generate_reply()
        # 判断是否转人工或挂机
        # await self.transfer_hangup(conversation, session_id, result)
        # if "挂机" in self.transfer_hangup or "转人" in self.transfer_hangup:
        #     result = result + "transfer_hangup:" + self.transfer_hangup
        logger.info(f"session_id:{self.session_id},最终生成话术是:{result}")
        return result

    def stream_reply(self, context, user_info, session_id):
        self.user_info = user_info
        conversation = self.format_conversation_history(context)
        logger.info("conversation is " + str(conversation) + " session_id is " + str(session_id))
        self.session_id = session_id
        self.context = context
        self.conversation_history_str = conversation

        if True:
            extract_user_info = self.extract_user_info(session_id)
            get_user_question = self.get_user_question()
            # if ("距离太远" in self.user_question) or ("距离问题" in self.user_question) or (
            #         "太远" in self.user_question):
            #     await self.distance_problem_agent(conversation, session_id)
            # await self.determine_solution(conversation, session_id)

            sales_skill = self.sales_skill(conversation, session_id)
            # 并行执行 用户情景、用户问题、销售技巧
            #asyncio.gather(extract_user_info, get_user_question, sales_skill)
            tasks_1 = [extract_user_info, get_user_question, sales_skill]
            #loop = asyncio.new_event_loop()
            #asyncio.set_event_loop(loop)
            # tasks_1 = [chat_bot.get_reply_by_stream(message, user_id)]
            loop = asyncio.get_event_loop()
            loop.run_until_complete(asyncio.gather(*tasks_1))

            # 查询知识库
            self.query_background_knowledge()
        else:
            return "喂，您好，您好，我这边是泰康高品质养老社区体验官，给您来电是想邀请您和家人来参观咱们泰康的高品质养老社区，提前为养老做规划！耽误您2分钟简单给您做个介绍好吧？".encode()
            # self.background_knowledge = ""
            # self.process_knowledge = "喂，您好，您好，我这边是泰康高品质养老社区体验官，给您来电是想邀请您和家人来参观咱们泰康的高品质养老社区，提前为养老做规划！耽误您2分钟简单给您做个介绍好吧？"
            # self.solution = ""
        logger.info(
            f"session_id:{self.session_id},Sales Skill: {self.process_knowledge},Determine Solution: {self.background_knowledge}")

        result = self.generate_reply()
        # 判断是否转人工或挂机
        # await self.transfer_hangup(conversation, session_id, result)
        # if "挂机" in self.transfer_hangup or "转人" in self.transfer_hangup:
        #     result = result + "transfer_hangup:" + self.transfer_hangup
        logger.info(f"session_id:{self.session_id},最终生成话术是:{result}")
        return result

    def query_background_knowledge(self):
        self.background_knowledge = ""
        if "无问题" not in self.user_question:
            keywords = ["位置", "附近", "地点", "地址", "生活资源", "交通资源", "旅游资源"]
            if any(keyword in self.user_question for keyword in keywords):
                if self.is_json(self.user_info):
                    json_object = json.loads(self.user_info)
                    if "城市" in json_object:
                        self.user_question = self.user_question + "-" + json_object["城市"]
                        print(json_object["城市"])
                        print(ProvinceCityDict().get_province(json_object["城市"]))
                        if ProvinceCityDict().get_province(json_object["城市"]) is not None:
                            self.user_question = self.user_question + "-" + ProvinceCityDict().get_province(
                                json_object["城市"])
                else:
                    self.user_question = self.user_question + "-" + self.city
            similarity_knowledge = self.knowledge_base_query.search_with_score("用户问题:" + self.user_question, top_k=6,
                                                                               limit_score=1.1)
            # process_knowledge = self.knowledge_base_query.search_with_score("主流程:", top_k=10, limit_score=0.9)
            background_knowledge = []
            if similarity_knowledge:
                # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
                for item in similarity_knowledge:
                    background_knowledge.append(item.page_content)
                    if len("".join(background_knowledge)) > 1024:
                        break
            self.background_knowledge = background_knowledge
            logger.info(f"session_id:{self.session_id},查询出的知识库内容是:{self.background_knowledge}")

    def is_json(self, myjson):
        try:
            json_object = json.loads(myjson)
        except ValueError as e:
            return False
        return True


async def parse_res_for_json(res, session_id):
    try:
        if type(res) == str and res != 'abandon':
            res = json.loads(res)
        else:
            res = {'transfer_flag': False, 'hangup_flag': False, 'transfer_reason': ''}
    except Exception as e:
        logger.warning(f"session_id:[{session_id}] transfer_hangup 解析json失败 e: {e}")
        # 目前json解析失败的都是这种 ```json ```格式的字符串
        try:
            json_str = str(res).replace("```json", "").replace("```", "")
            res = json.loads(json_str)
        except:
            logger.warning(f"session_id:[{session_id}] transfer_hangup 解析json失败 第二次失败 e: {e}")
            res = {'transfer_flag': False, 'hangup_flag': False, 'transfer_reason': ''}
    return res


class SaleGPT:
    def getSales(self):
        return Sales()

    def reply(self, query, session_id=None):
        if session_id not in cache_dict:
            cache_dict[session_id] = []
        if 'user_info-' + session_id not in cache_dict:
            cache_dict['user_info-' + session_id] = []
        conversation_history = cache_dict.get(session_id)
        user_info = cache_dict.get('user_info-' + session_id)
        if len(conversation_history) > 0:
            conversation_dict = {}
            conversation_dict["role"] = "user"
            conversation_dict["content"] = query
            cache_dict[session_id].append(conversation_dict)

        sales = self.getSales()
        result = asyncio.run(sales.async_reply(conversation_history, user_info, session_id))
        conversation_dict = {}
        conversation_dict["role"] = "assistant"
        conversation_dict["content"] = result
        cache_dict[session_id].append(conversation_dict)
        return result


class SaleGPTSelf(SaleGPT):
    def getSales(self, mode_name='Colossal-LLaMA-2-7b_20231220'):
        sales = Sales()
        sales.model_name = mode_name
        return sales


class SaleGPTSteam(SaleGPTSelf):
    def getSales(self, mode_name='Colossal-LLaMA-2-7b_20231220'):
        sales = Sales()
        sales.model_name = mode_name
        sales.steam = True
        return sales

    def flush_conversation(self, result_content, session_id=None):
        conversation_dict = {}
        conversation_dict["role"] = "assistant"
        conversation_dict["content"] = result_content
        cache_dict[session_id].append(conversation_dict)

    def reply_by_steam(self, query, session_id=None):
        if session_id not in cache_dict:
            cache_dict[session_id] = []
        if 'user_info-' + session_id not in cache_dict:
            cache_dict['user_info-' + session_id] = []
        conversation_history = cache_dict.get(session_id)
        user_info = cache_dict.get('user_info-' + session_id)
        if len(conversation_history) > 0:
            conversation_dict = {}
            conversation_dict["role"] = "user"
            conversation_dict["content"] = query
            cache_dict[session_id].append(conversation_dict)

        sales = self.getSales()
        return sales.stream_reply(conversation_history, user_info, session_id)

        # result = ""
        # reply_str = asyncio.run(sales.stream_reply(conversation_history, user_info, session_id))
        # for text in reply_str:
        #    yield text
        #    result += str(text)
        # conversation_dict = {}
        # conversation_dict["role"] = "assistant"
        # conversation_dict["content"] = str(result)
        # cache_dict[session_id].append(conversation_dict)


if __name__ == '__main__':
    session_id = '123'
    conversation_history = []
    while True:
        if 'user_info-' + session_id not in cache_dict:
            cache_dict['user_info-' + session_id] = []
        user_info = cache_dict.get('user_info-' + session_id)

        sales = SaleGPTSelf().getSales()
        result = asyncio.run(sales.async_reply(conversation_history, user_info, session_id))
        conversation_dict = {}
        conversation_dict["role"] = "assistant"
        conversation_dict["content"] = result
        conversation_history.append(conversation_dict)
        print(result)
        user_input = input("请输入用户的对话：")
        conversation_dict = {}
        conversation_dict["role"] = "user"
        conversation_dict["content"] = user_input
        conversation_history.append(conversation_dict)
        print(conversation_history)

#####################################################################
#     conversation_template = """
#     坐席: 喂，你好。
# 坐席: 哎，你好，我这边是泰康高频质养老社区的体验，官哈呃，给您来电的是想邀请您和您的家人呢？来参观咱们泰康高品质养老社区，那提前为咱们的养老的做一个规划，嗯，就耽误您两分钟时间，简单的给您做一个介绍好吧。
# 用户: 没有。
# 用户: 嗯，
# 坐席: 嗯，咱们泰康养老社区呢，是采用国际最先进的医养农合力养老的方式，那社区呢是有自己的医院，像各种兴趣八呀，娱乐设施这些都有的，让我们的养老生活呀，更加丰富多彩，呃，平均寿命的，能长个八到十岁呃，咱们的月费呢？大概是在一万左右，现在呢，是有免费参观的名额，呃，那我这给您发送一个邀请，哈哈。
# 坐席: 嗯，呃，我听您的声音还是挺年轻的，哈嗯，冒昧的，问一下，您今年多大了呀。
# 用户: 啊，我还不着急，我才五十几，我七零年的
# 坐席: 嗯，理解的，哈呃，不过养老呢，都是需要咱们提前考虑的呀，等退休之后呢，就能享受健康富足长寿的晚年生活，您说对吧？呃，官方统计入住高端养老社区呢，要长寿八到十年的呃，您可以先了解一下的好吧。
# 用户: 啊，不着急，这个
# """
#
#     def generate_reply():
#         try:
#             generatetestset = GenerateTestSet(conversation_template, conversation_history)
#             result = asyncio.run(generatetestset.achat_with_proxy_gpt4(save_data=False))
#             return result
#         except Exception as e:
#             # 打印错误的堆栈信息
#             logger.exception(e)
#             return "出错了，我不知道怎么回答你的问题，你可以换个问法试试"
#     for i in range(10):
#         if "user_info" not in cache_dict:
#             cache_dict["user_info"] = []
#         user_info = cache_dict.get("user_info")
#         sales = Sales()
#         result = asyncio.run(sales.async_reply(conversation_history, user_info, "123"))
#         conversation_dict = {}
#         conversation_dict["role"] = "assistant"
#         conversation_dict["content"] = result
#         conversation_history.append(conversation_dict)
#         print(result)
#         if "挂机" in result or "再见" in result:
#             break
#         # 模拟用户话术
#         # generatetestset = GenerateTestSet(conversation_template, conversation_history)
#         reply = generate_reply()
#         user_input = reply
#         conversation_dict = {}
#         conversation_dict["role"] = "user"
#         conversation_dict["content"] = user_input
#         conversation_history.append(conversation_dict)
#     # print(conversation_history)
#     content_list = [i['content'] for i in conversation_history]
#     content_str = '\n'.join(content_list)
#     print(content_str)
